@page "/system/picklist"
@using Blazor.Server.UI.Components.Dialogs
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Commands.AddEdit
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Commands.Delete
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Commands.Import
@using CleanArchitecture.Blazor.Application.Features.KeyValues.DTOs
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Queries.ByName
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Queries.Export
@attribute [Authorize(Policy = Permissions.Dictionaries.View)]
@inject IStringLocalizer<Dictionaries> L
@inject IJSRuntime JS
<PageTitle>@Title</PageTitle>
<style>
    .mud-table-toolbar {
        height: 84px !important;
    }
</style>
<ErrorBoundary>
    <ChildContent>

        <MudTable @ref="_table"
                  Items="@KeyValueList"
                  FixedHeader="true"
                  FixedFooter="true"
                  Virtualize="true"
                  RowsPerPage="25"
                  Height="calc(100vh - 265px)"
                  Hover="true"
                  MultiSelection="true"
                  @bind-SelectedItems="_selectedItems"
                  @bind-SelectedItem="_selectedItem"
                  CommitEditTooltip="@L["Commit Edit"]"
                  RowEditPreview="BackupItem"
                  RowEditCancel="ResetItemToOriginalValues"
                  CanCancelEdit="@_canCancelEdit"
                  RowEditCommit="ItemHasBeenCommitted"
                  OnCommitEditClick="@(() => Snackbar.Add(L["Update successfully"], MudBlazor.Severity.Info))"
                  IsEditRowSwitchingBlocked="false"
                  ReadOnly="@_readonly"
                  Loading="@_loading"
                  SortLabel="@L["Sort By"]"
                  Filter="new Func<KeyValueDto,bool>(_quickFilter)">
            <ToolBarContent>
                <div class="justify-start pt-3">
                    <MudText Typo="Typo.h6">@L["Picklist"]</MudText>
                    <MudHidden Breakpoint="Breakpoint.SmAndDown">
                    <MudButton DisableElevation Variant="Variant.Outlined"
                               Size="Size.Small"
                               OnClick="OnRefresh"
                               Disabled="@_loading"
                               StartIcon="@Icons.Material.Filled.Refresh" IconColor="Color.Surface" Color="Color.Primary"
                               Style="margin-right: 4px; margin-bottom:4px">@L["Refresh"]</MudButton>
                    @if (_canCreate)
                    {
                        <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Primary"
                               Disabled="@_editing"
                               StartIcon="@Icons.Material.Filled.Add"
                               Size="Size.Small"
                               OnClick="OnCreate"
                               Style="margin-right: 4px; margin-bottom:4px"
                               IconColor="Color.Surface">@L["Create"]</MudButton>
                    }
                    @if (_canDelete)
                    {
                        <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Secondary"
                               StartIcon="@Icons.Material.Filled.Delete"
                               Disabled="@(!(_selectedItems.Count>0) || _editing)"
                               OnClick="OnDeleteChecked"
                               Size="Size.Small"
                               Style="margin-right: 4px; margin-bottom:4px"
                               IconColor="Color.Surface">@L["Delete"]</MudButton>
                    }
                    @if (_canImport)
                    {
                        <InputFile id="importdataInput" OnChange="OnImportData" hidden  accept=".xlsx" />
                        <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Primary"
                               StartIcon="@Icons.Material.Filled.Upload"
                               Size="Size.Small"
                               Style="margin-right: 4px; margin-bottom:4px"
                               for="importdataInput"
                               HtmlTag="label"
                               IconColor="Color.Surface">@L["Import Data"]</MudButton>
                    }
                    @if (_canExport)
                    {
                        <MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Primary"
                               StartIcon="@Icons.Material.Filled.Download"
                               Size="Size.Small"
                               Style="margin-right: 4px; margin-bottom:4px"
                               OnClick="OnExport"
                               IconColor="Color.Surface">@L["Export Data"]</MudButton>
                    }
                    </MudHidden>
                    <MudHidden Breakpoint="Breakpoint.SmAndDown" Invert="true">
                        <MudMenu  AnchorOrigin="Origin.BottomLeft" StartIcon="@Icons.Filled.KeyboardCommandKey" EndIcon="@Icons.Filled.KeyboardArrowDown" Label="@L["Action"]" Color="Color.Primary" Variant="Variant.Filled">
                            <MudMenuItem Disabled="@_loading" OnClick="@(()=>OnRefresh())">@L["Refresh"]</MudMenuItem>
                            @if (_canCreate)
                            {
                                <MudMenuItem Disabled="@_editing" OnClick="OnCreate">@L["Create"]</MudMenuItem>
                            }
                            @if (_canDelete)
                            {
                                <MudMenuItem Disabled="@(!(_selectedItems.Count>0) || _editing)" OnClick="OnDeleteChecked">@L["Delete"]</MudMenuItem>
                            }
                            @if (_canImport)
                            {   <InputFile id="importdataInput" OnChange="OnImportData" hidden accept=".xlsx" />
                                <MudMenuItem for="importdataInput"
                                   HtmlTag="label">@L["Import Data"]</MudMenuItem>
                            }
                            @if (_canExport)
                            {
                                <MudMenuItem OnClick="OnExport">@L["Export Data"]</MudMenuItem>
                            }
                        </MudMenu>
                    </MudHidden>
                </div>
                <MudSpacer />
                @if (_canSearch)
                {
                    <MudTextField @bind-Value="_searchString" Immediate="true" FullWidth="false"
                              Placeholder="@(L["Search for role name"])" Adornment="Adornment.End"
                              AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0 mb-3">
                    </MudTextField>
                }
            </ToolBarContent>
            <ColGroup>
                <col style="width:50px;" />
            </ColGroup>
            <HeaderContent>
                <MudTh><MudTableSortLabel SortBy="new Func<KeyValueDto, object>(x=>x.Name)">@L["Name"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<KeyValueDto, object>(x=>x.Value)">@L["Value"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<KeyValueDto, object>(x=>x.Text)">@L["Text"]</MudTableSortLabel></MudTh>
                <MudTh><MudTableSortLabel SortBy="new Func<KeyValueDto, object>(x=>x.Description)">@L["Description"]</MudTableSortLabel></MudTh>
            </HeaderContent>
            <RowTemplate>
                <MudTd DataLabel="@L["Name"]">@context.Name</MudTd>
                <MudTd DataLabel="@L["Value"]">@context.Value</MudTd>
                <MudTd DataLabel="@L["Text"]">@context.Text</MudTd>
                <MudTd DataLabel="@L["Description"]">@context.Description</MudTd>
            </RowTemplate>
            <RowEditingTemplate>
                <MudTd DataLabel="Name">
                   <MudAutocomplete @bind-Value="@context.Name" Clearable="true" CoerceText="true" CoerceValue="true" SearchFunc="searchName" Required />
                </MudTd>
                <MudTd DataLabel="Value">
                    <MudTextField @bind-Value="@context.Value" Required />
                </MudTd>
                <MudTd DataLabel="Text">
                    <MudTextField @bind-Value="@context.Text" Required />
                </MudTd>
                <MudTd DataLabel="Description">
                    <MudTextField @bind-Value="@context.Description" />
                </MudTd>
                
            </RowEditingTemplate>
            <PagerContent>
                <MudTablePager />
            </PagerContent>
        </MudTable>
    </ChildContent>
    <ErrorContent>
        <CustomError Exception="context" ></CustomError>
    </ErrorContent>
</ErrorBoundary>

@code {
    private MudTable<KeyValueDto> _table=default!;
    public string Title { get; set; } = "Dictionaries";
    private IList<KeyValueDto> KeyValueList = new List<KeyValueDto>();
    private HashSet<KeyValueDto> _selectedItems = new HashSet<KeyValueDto>();
    private KeyValueDto? _selectedItem { get; set; } = null;
    private KeyValueDto _elementBeforeEdit { get; set; } = new();
    private bool _canCancelEdit=true;
    private string _searchString = string.Empty;
    private bool _sortNameByLength;
    private bool _editing;
    [CascadingParameter]
    private Task<AuthenticationState> AuthState { get; set; } = default!;
    [Inject]
    private ISender _mediator { get; set; } = default!;
    private bool _canCreate;
    private bool _canSearch;
    private bool _canEdit;
    private bool _canDelete;
    private bool _canImport;
    private bool _canExport;
    private bool _readonly => !_canCreate || !_canEdit;
    private bool _loading;
    protected override async Task OnInitializedAsync()
    {
        var state = await AuthState;
        _canCreate = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Create)).Succeeded;
        _canSearch = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Search)).Succeeded;
        _canEdit = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Edit)).Succeeded;
        _canDelete = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Delete)).Succeeded;
        _canImport = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Import)).Succeeded;
        _canExport = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Export)).Succeeded;
        await LoadData();
    }
    private bool _quickFilter(KeyValueDto dto) => FilterFunc(dto, _searchString);

    private bool FilterFunc(KeyValueDto dto, string searchString)
    {
        if (string.IsNullOrWhiteSpace(searchString))
            return true;
        if (dto.Name.Contains(searchString, StringComparison.OrdinalIgnoreCase))
            return true;
        if (dto.Description.Contains(searchString, StringComparison.OrdinalIgnoreCase))
            return true;
        if ($"{dto.Value} {dto.Text}".Contains(searchString))
            return true;
        return false;
    }
    private Task<IEnumerable<string>> searchName(string value)
    {
        if (string.IsNullOrEmpty(value))
            return Task.FromResult(KeyValueList.Where(x=>!string.IsNullOrEmpty(x.Name)).Select(x=>x.Name).OrderBy(x=>x).Distinct());
        return Task.FromResult(KeyValueList.Where(x =>!string.IsNullOrEmpty(x.Name) && x.Name.Contains(value, StringComparison.InvariantCultureIgnoreCase)).Select(x=>x.Name).OrderBy(x=>x).Distinct());
    }
    private async Task LoadData()
    {
        if(_loading) return;
        try{
            _loading = true;
            _editing = false;
            var cmd = new GetAllKeyValuesQuery();
            KeyValueList = (await _mediator.Send(cmd).ConfigureAwait(false)).ToList();
            _selectedItems=new HashSet<KeyValueDto>();;
            _selectedItem = null;
        }
        finally
        {
            _loading = false;
        }


    }
    private async Task OnRefresh()
    {
        _searchString = string.Empty;
        await LoadData();
    }
    private async Task OnDeleteChecked()
    {
        var deleteContent = L["You're sure you want to delete the selected items:{0}?"];
        var parameters = new DialogParameters
        {
            { nameof(DeleteConfirmation.ContentText), string.Format(deleteContent,_selectedItems.Count) }
        };
        var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall, FullWidth = true, DisableBackdropClick = true };
        var dialog = DialogService.Show<DeleteConfirmation>(L["Delete"], parameters, options);
        var state = await dialog.Result;
        if (!state.Cancelled)
        {
            var command = new DeleteKeyValueCommand(_selectedItems.Select(x => x.Id).ToArray());
            var result = await _mediator.Send(command);
            await LoadData();
            Snackbar.Add($"{L["Deleted successfully"]}", MudBlazor.Severity.Info);
        }
    }
    private Task OnCreate()
    {
        _editing = true;
        var newitem = new KeyValueDto()
        {
            Name = _selectedItem?.Name,
            Description = _selectedItem?.Description
        };
        KeyValueList.Insert(0, newitem);
        return Task.CompletedTask;
    }
    private void BackupItem(object element)
    {
        InvokeAsync(() =>
        {
            _editing = true;
            _elementBeforeEdit = new()
                {
                    Id = ((KeyValueDto)element).Id,
                    Name = ((KeyValueDto)element).Name,
                    Text = ((KeyValueDto)element).Text,
                    Value = ((KeyValueDto)element).Value,
                    Description = ((KeyValueDto)element).Description,
                };
                StateHasChanged();
        });
    }
    private void ResetItemToOriginalValues(object element)
    {
        InvokeAsync(() =>
        {
            ((KeyValueDto)element).Id = _elementBeforeEdit.Id;
            ((KeyValueDto)element).Name = _elementBeforeEdit.Name;
            ((KeyValueDto)element).Text = _elementBeforeEdit.Text;
            ((KeyValueDto)element).Value = _elementBeforeEdit.Value;
            ((KeyValueDto)element).Description = _elementBeforeEdit.Description;

            _editing = false;
            StateHasChanged();
        });

    }
    private void ItemHasBeenCommitted(object element)
    {
        InvokeAsync(async () =>
        {
            var command = new AddEditKeyValueCommand()
                {
                    Id = ((KeyValueDto)element).Id,
                    Name = ((KeyValueDto)element).Name,
                    Text = ((KeyValueDto)element).Text,
                    Value = ((KeyValueDto)element).Value,
                    Description = ((KeyValueDto)element).Description,
                };
            var result = await _mediator.Send(command);
            ((KeyValueDto)element).Id = result.Data;
            _editing = false;
            StateHasChanged();
        });
    }

    private async Task OnExport()
    {

        var request = new ExportKeyValuesQuery()
            {
                Keyword = _searchString,
                OrderBy = string.IsNullOrEmpty(_table.TableContext.SortFieldLabel) ? "Id" : _table.TableContext.SortFieldLabel,
                SortDirection = (_table.TableContext.SortDirection == SortDirection.None ? SortDirection.Descending.ToString() : _table.TableContext.SortDirection.ToString()),
            };
        var result = await _mediator.Send(request);
        using var streamRef = new DotNetStreamReference(new MemoryStream(result));
        await JS.InvokeVoidAsync("downloadFileFromStream", $"{L["Dictionaries"]}.xlsx", streamRef);
    }
    private async Task OnImportData(InputFileChangeEventArgs e)
    {
        var stream = new MemoryStream();
        await e.File.OpenReadStream().CopyToAsync(stream);
        var command = new ImportKeyValuesCommand(e.File.Name, stream.ToArray());
        var result = await _mediator.Send(command);
        if(result.Succeeded){
            await OnRefresh();
            Snackbar.Add($"Import data successfully", MudBlazor.Severity.Info);
        }
        else
        {
            foreach(var msg in result.Errors)
            {
                Snackbar.Add($"{msg}", MudBlazor.Severity.Error);
            }
        }
    }

}
